/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <vector>
#include <mutex>
#include <condition_variable>
#include "comm_function.h"
#include "mars/comm/xlogger/xlogger.h"
#include "assert/__assert.h"
#include "var_cache.h"
#include "autobuffer.h"
#include "mars/comm/xlogger/android_xlog.h"
#include "fmt_args_parser.h"

#include "hilog/log.h"
#define LOG_DOMAIN 0x0201
#define LOG_TAG "MY_TAG"

//Get the last error information
void getExceptionSummary(napi_env env, std::string& info)
{
    const napi_extended_error_info* error_info = NULL;
    napi_get_last_error_info(env, &error_info);
    if (error_info->error_code == napi_ok) {
        info.clear();
        return;
    }
    info = error_info->error_message;
}
/*
 * Formats an exception as a string with its stack trace.
 */
int printStackTrace(napi_env env, std::string& str)
{
    napi_value info = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.CommonError", "printStackTrace", 0);

    int ret = GetNAPIStringValue(env, info, str);
    return ret;
}

/*
 * Log an exception.
 */
/*
static void jsLogException(napi_env env)
{
    const napi_extended_error_info* error_info = NULL;
    napi_get_last_error_info((env), &error_info);
	if (error_info->error_code == napi_ok) {
		return;
	}
	//printf("mars::napiexception: %s\n", error_info->error_message);	
    __android_log_write(ANDROID_LOG_WARN, "mars::jniexception", error_info->error_message);
}
*/
napi_value JS_CallMethodByName(napi_env env, napi_value class_or_obj, const std::string& method_name, int argc, ...)
{
    napi_value method_value;
    napi_status status = napi_get_named_property(env, class_or_obj, method_name.c_str(), &method_value);
    if (status != napi_ok) {
        return nullptr;
    }

    napi_value* argv = nullptr;
    if (argc > 0) {
        argv = new napi_value[argc];
        va_list ap;
        va_start(ap, argc);
        for (int idx = 0; idx < argc; ++idx) {
            argv[idx] = va_arg(ap, napi_value);
        }
    }
    napi_value result;
    status = napi_call_function(env, class_or_obj, method_value, argc, argv, &result);
    if (argv != nullptr) {
        delete[] argv;
    }
    return result;
}

napi_value JS_CallMethodByName(napi_env env, napi_value class_or_obj, const std::string& method_name, const char* fmt, ...)
{
    napi_value method_value;
    napi_status status = napi_get_named_property(env, class_or_obj, method_name.c_str(), &method_value);
    if (status != napi_ok) {
        return nullptr;
    }

    std::vector < FMTArgsPaser::FMT_TOKEN_TYPE > fmt_vec;
    FMTArgsPaser parser(fmt);
    int ret = parser.Parse(fmt_vec);
    if (ret != 0) {
        return nullptr;
    }

    napi_value* argv = NULL;
    int argc = fmt_vec.size();

    if (argc != 0) {
        argv = new napi_value[argc];
        va_list ap;
        va_start(ap, fmt);
        for (int idx = 0; idx < argc; ++idx) {
            napi_value anArg;
            napi_status status;
            switch (fmt[idx]) {
                case FMTArgsPaser::INT32_FMT: {
                    int32_t value = va_arg(ap, int32_t);
                    status = napi_create_int32(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::INT64_FMT: {
                    int64_t value = va_arg(ap, int64_t);
                    status = napi_create_int64(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UINT32_FMT: {
                    uint32_t value = va_arg(ap, uint32_t);
                    status = napi_create_uint32(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UINT64_FMT: {
                    uint64_t value = va_arg(ap, uint64_t);
                    status = napi_create_int64(env, static_cast < int64_t > (value), &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::DOUBLE_FMT: {
                    double value = va_arg

                    (ap,  double);
                    status = napi_create_double(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::LOWER_S_FMT: {
                    const char* value = va_arg(ap, const char*);
                    status = napi_create_string_utf8(env,
                            value,
                            NAPI_AUTO_LENGTH,
                            &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UPPER_S_FMT: {
                    const std::string* value = va_arg(ap, const std::string*);
                    status = napi_create_string_utf8(env,
                            value->c_str(),
                            value->length(),
                            &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::NAPI_VALUE_FMT: {
                    anArg = va_arg(ap, napi_value);
                    break;
                }
            }
            argv[idx] = anArg;
        }
    }
    napi_value result;
    status = napi_call_function(env, class_or_obj, method_value, argc, argv, &result);
    if (argv != nullptr) {
        delete[] argv;
    }
    return result;
}
napi_value JS_FindMethodByName(napi_env env, napi_value class_or_obj, const std::string& method_name)
{
    napi_value method_value;
    napi_status status = napi_get_named_property(env, class_or_obj, method_name.c_str(), &method_value);
    if (status != napi_ok) {
        return nullptr;
    }
    return method_value;
}

napi_value JS_CallMethodByName(napi_env env, napi_value class_or_obj, napi_value method, int argc, ...)
{
    napi_value* argv = nullptr;
    if (argc > 0) {
        argv = new napi_value[argc];
        va_list ap;
        va_start(ap, argc);
        for (int idx = 0; idx < argc; ++idx) {
            argv[idx] = va_arg(ap, napi_value);
        }
    }

    napi_value result;
    napi_status status = napi_call_function(env, class_or_obj, method, argc, argv, &result);
    if (argv != nullptr) {
        delete[] argv;
    }
    return result;
}

napi_value JS_CallMethodByName(napi_env env, napi_value class_or_obj, napi_value method, const char* fmt, ...)
{
    std::vector < FMTArgsPaser::FMT_TOKEN_TYPE > fmt_vec;
    FMTArgsPaser parser(fmt);
    int ret = parser.Parse(fmt_vec);
    if (ret != 0) {
        return nullptr;
    }

    napi_value* argv = NULL;
    int argc = fmt_vec.size();

    if (argc != 0) {
        argv = new napi_value[argc];
        va_list ap;
        va_start(ap, fmt);
        for (int idx = 0; idx < argc; ++idx) {
            napi_value anArg;
            napi_status status;
            switch (fmt[idx]) {
                case FMTArgsPaser::INT32_FMT: {
                    int32_t value = va_arg(ap, int32_t);
                    status = napi_create_int32(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::INT64_FMT: {
                    int64_t value = va_arg(ap, int64_t);
                    status = napi_create_int64(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UINT32_FMT: {
                    uint32_t value = va_arg(ap, uint32_t);
                    status = napi_create_uint32(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UINT64_FMT: {
                    uint64_t value = va_arg(ap, uint64_t);
                    status = napi_create_int64(env, static_cast < int64_t > (value), &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::DOUBLE_FMT: {
                    double value = va_arg

                    (ap,  double);
                    status = napi_create_double(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::LOWER_S_FMT: {
                    const char* value = va_arg(ap, const char*);
                    status = napi_create_string_utf8(env,
                            value,
                            NAPI_AUTO_LENGTH,
                            &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UPPER_S_FMT: {
                    const std::string* value = va_arg(ap, const std::string*);
                    status = napi_create_string_utf8(env,
                            value->c_str(),
                            value->length(),
                            &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::NAPI_VALUE_FMT: {
                    anArg = va_arg(ap, napi_value);
                    break;
                }
            }
            argv[idx] = anArg;
        }
    }
    napi_value result;
    napi_status status = napi_call_function(env, class_or_obj, method, argc, argv, &result);
    if (argv != nullptr) {
        delete[] argv;
    }
    return result;
}
napi_value JS_CallStaticMethodByName(napi_env env, const std::string& class_name, const std::string& method_name, int argc, ...)
{
    napi_value result;
    napi_status status;
    VarCache * instance = VarCache::Singleton();
    napi_value klass = instance->GetOneClass(class_name.c_str());
    OH_LOG_INFO(LOG_APP, "JS_CallStaticMethodByName class_name: %s, class:%p, method:%s, argc:%d", class_name.c_str(), klass, method_name.c_str(), argc);
    if (klass == nullptr) {
        return nullptr;
    }
//    napi_value method = instance->GetOnePropertyOfObj(klass, method_name.c_str());
//  napi_value method = instance->GetOnePropertyOfClass(class_name, method_name);
	napi_value method = nullptr;
	if (napi_ok != (status = napi_get_named_property(env, klass, method_name.c_str(), &method))) {
		OH_LOG_INFO(LOG_APP, "napi_get_named_property status:%d", status);
	}

    if (method == nullptr) {
        OH_LOG_ERROR(LOG_APP, "JS_CallStaticMethodByName GetOnePropertyOfObj method_name:%s, method:%p", method_name.c_str(), method);
        return nullptr;
    }
	
	napi_ref method_ref = 0;
	napi_create_reference(env, method, 1, &method_ref);

    napi_value* argv = nullptr;
    if (argc > 0) {
        argv = new napi_value[argc];
        OH_LOG_INFO(LOG_APP, "create argv with new array %d", argc);
        va_list ap;
        va_start(ap, argc);
        for (int idx = 0; idx < argc; ++idx) {
            argv[idx] = va_arg(ap, napi_value);
            OH_LOG_INFO(LOG_APP, "argv[%d]: %d", idx, argv[idx]);
        }
    }
    OH_LOG_INFO(LOG_APP, "JS_CallStaticMethodByName napi_call_function env:%x, klass:%x, method:%x, argc:%d, argv:%x", env, klass, method, argc, argv);
    status = napi_call_function(env, klass, method, argc, argv, &result);
    OH_LOG_INFO(LOG_APP, "JS_CallStaticMethodByName class_name: %s, method:%s, napi_call_function status:%d", class_name.c_str(), method_name.c_str(), status);
    if (argv != nullptr) {
        delete[] argv;
    }
    return result;
}

napi_value JS_CallStaticMethodByName(napi_env env, const std::string& class_name, const std::string& method_name, const char* fmt, ...)
{
    VarCache * instance = VarCache::Singleton();
    napi_value klass = instance->GetOneClass(class_name.c_str());
    if (klass == nullptr) {
        return nullptr;
    }
//    napi_value method = instance-> GetOnePropertyOfClass(class_name, method_name);
    napi_value method = instance->GetOnePropertyOfObj(klass, method_name.c_str());
    if (method == nullptr) {
        return nullptr;
    }

    std::vector < FMTArgsPaser::FMT_TOKEN_TYPE > fmt_vec;
    FMTArgsPaser parser(fmt);
    int ret = parser.Parse(fmt_vec);
    if (ret != 0) {
        return nullptr;
    }

    napi_value* argv = NULL;
    int argc = fmt_vec.size();

    if (argc != 0) {
        argv = new napi_value[argc];
        va_list ap;
        va_start(ap, fmt);
        for (int idx = 0; idx < argc; ++idx) {
            napi_value anArg;
            napi_status status;
            switch (fmt[idx]) {
                case FMTArgsPaser::INT32_FMT: {
                    int32_t value = va_arg(ap, int32_t);
                    status = napi_create_int32(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::INT64_FMT: {
                    int64_t value = va_arg(ap, int64_t);
                    status = napi_create_int64(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UINT32_FMT: {
                    uint32_t value = va_arg(ap, uint32_t);
                    status = napi_create_uint32(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UINT64_FMT: {
                    uint64_t value = va_arg(ap, uint64_t);
                    status = napi_create_int64(env, static_cast < int64_t > (value), &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::DOUBLE_FMT: {
                    double value = va_arg

                    (ap,  double);
                    status = napi_create_double(env, value, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::LOWER_S_FMT: {
                    const char* value = va_arg(ap, const char*);
                    status = napi_create_string_utf8(env, value, NAPI_AUTO_LENGTH, &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::UPPER_S_FMT: {
                    const std::string* value = va_arg(ap, const std::string*);
                    status = napi_create_string_utf8(env, value->c_str(), value->length(),
                            &anArg);
                    if (status != napi_ok) {
                        return nullptr;
                    }
                    break;
                }
                case FMTArgsPaser::NAPI_VALUE_FMT: {
                    anArg = va_arg(ap, napi_value);
                    break;
                }
            }
            argv[idx] = anArg;
        }
    }

    napi_value result;
    napi_status status = napi_call_function(env, klass, method, argc, argv, &result);
    if (argv != nullptr) {
        delete[] argv;
    }
    return result;
}

napi_value JS_GetProperty(napi_env env, napi_value class_or_obj, const std::string& property_name)
{
    VarCache * instance = VarCache::Singleton();
    napi_value result;
    napi_status status = napi_get_named_property(env, class_or_obj, property_name.c_str(), &result);
    if (status != napi_ok) {
        return nullptr;
    } else {
        return result;
    }
}
int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, int32_t* value)
{
    napi_value aValue = JS_GetProperty(env, obj_or_class, property_name);
    if (aValue == nullptr) {
        return -1;
    } else {
        napi_status status = napi_get_value_int32(env, aValue, value);
        if (status != napi_ok) {
            return -2;
        } else {
            return 0;
        }
    }
}
int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, bool* value)
{
    napi_value aValue = JS_GetProperty(env, obj_or_class, property_name);
    if (aValue == nullptr) {
        return -1;
    } else {
        napi_status status = napi_get_value_bool(env, aValue, value);
        if (status != napi_ok) {
            return -2;
        } else {
            return 0;
        }
    }
}
int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, bool& value)
{
    return JS_GetProperty(env, obj_or_class, property_name, &value);
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, int32_t& value)
{
    return JS_GetProperty(env, obj_or_class, property_name, &value);
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, uint32_t* value)
{
    napi_value aValue = JS_GetProperty(env, obj_or_class, property_name);
    if (aValue == nullptr) {
        return -1;
    } else {
        napi_status status = napi_get_value_uint32(env, aValue, value);
        if (status != napi_ok) {
            return -2;
        } else {
            return 0;
        }
    }
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, uint32_t& value)
{
    return JS_GetProperty(env, obj_or_class, property_name, &value);
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, int64_t* value)
{
    napi_value aValue = JS_GetProperty(env, obj_or_class, property_name);
    if (aValue == nullptr) {
        return -1;
    } else {
        napi_status status = napi_get_value_int64(env, aValue, value);
        if (status != napi_ok) {
            return -2;
        } else {
            return 0;
        }
    }
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, int64_t& value)
{
    return JS_GetProperty(env, obj_or_class, property_name, &value);
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, double* value)
{
    napi_value aValue = JS_GetProperty(env, obj_or_class, property_name);
    if (aValue == nullptr) {
        return -1;
    } else {
        napi_status status = napi_get_value_double(env, aValue, value);
        if (status != napi_ok) {
            return -2;
        } else {
            return 0;
        }
    }
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, double& value)
{
    return JS_GetProperty(env, obj_or_class, property_name, &value);
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, std::string& value)
{
    napi_value aValue = JS_GetProperty(env, obj_or_class, property_name);
    if (aValue == nullptr) {
        return -1;
    } else {
        int status = GetNAPIStringValue(env, aValue, value);
        if (status != napi_ok) {
            return -2;
        } else {
            return 0;
        }
    }
}

int JS_GetProperty(napi_env env, napi_value obj_or_class, const std::string& property_name, std::string* value)
{
    return JS_GetProperty(env, obj_or_class, property_name, *value);
}

int JS_GetAllProperties(napi_env env, napi_value class_or_obj, std::map < std::string, napi_value >& property_map)
{
    VarCache * instance = VarCache::Singleton();
    return VarCache::FindProperties(class_or_obj, property_map);
}

int JS_SetAField(napi_env env, napi_value object, const char* field_name, int32_t field_value)
{
    napi_value value;
    napi_status status = napi_create_int32(env, field_value, &value);
    if (status != napi_ok) {
        return status;
    }

    status = napi_set_named_property(env, object, field_name, value);
    return status;
}

int JS_SetAField(napi_env env, napi_value object, const char* field_name, uint32_t field_value)
{
    napi_value value;
    napi_status status = napi_create_uint32(env, field_value, &value);
    if (status != napi_ok) {
        return status;
    }

    status = napi_set_named_property(env, object, field_name, value);
    return status;
}
int JS_SetAField(napi_env env, napi_value object, const char* field_name, int64_t field_value)
{
    napi_value value;
    napi_status status = napi_create_int64(env, field_value, &value);
    if (status != napi_ok) {
        return status;
    }

    status = napi_set_named_property(env, object, field_name, value);
    return status;
}

int JS_SetAField(napi_env env, napi_value object, const char* field_name, uint64_t field_value)
{
    napi_value value;
    napi_status status = napi_create_int64(env, static_cast < int64_t > (field_value), &value);
    if (status != napi_ok) {
        return status;
    }

    status = napi_set_named_property(env, object, field_name, value);
    return status;
}
int JS_SetAField(napi_env env, napi_value object, const char* field_name, double field_value)
{
    napi_value value;
    napi_status status = napi_create_double(env, field_value, &value);
    if (status != napi_ok) {
        return status;
    }

    status = napi_set_named_property(env, object, field_name, value);
    return status;
}
int JS_SetAField(napi_env env, napi_value object, const char* field_name, const char* field_value)
{
    napi_value value;
    napi_status status = napi_create_string_utf8(env, field_value, NAPI_AUTO_LENGTH, &value);
    if (status != napi_ok) {
        return status;
    }

    status = napi_set_named_property(env, object, field_name, value);
    return status;
}
int JS_SetAField(napi_env env, napi_value object, const char* field_name, const std::string& field_value)
{
    napi_value value;
    napi_status status = napi_create_string_utf8(env, field_value.c_str(), field_value.length(), &value);
    if (status != napi_ok) {
        return status;
    }

    status = napi_set_named_property(env, object, field_name, value);
    return status;
}

/*Description: Get a class's field which has the specified name
	Notice:
		If you pass an object, you will get the object's field (i.e non-static field) rather than its class's field.
		If you pass a class, you will get the class's field field (i.e. static field) rather than its object's field.
 */
napi_value JS_GetStaticField(napi_env env, const std::string& class_name, const std::string& field_name)
{
    VarCache * instance = VarCache::Singleton();
    napi_value klass = instance->GetOneClass(class_name.c_str());
    //napi_value result = instance-> GetOnePropertyOfClass(class_name, field_name);
    napi_value result = instance->GetOnePropertyOfObj(klass, field_name.c_str());
    return result;
}

int JS_Object2Map(napi_env env, napi_value obj, std::map < std::string, std::string >& strMap)
{
    std::map < std::string, napi_value > property_map;
    int ret = VarCache::FindProperties(obj, property_map);
    if (ret == napi_ok) {
        std::map < std::string, napi_value >::iterator it;
        for (it = property_map.begin(); it != property_map.end(); ++it) {
            std::string str;
            ret = GetNAPIStringValue(env, it->second, str);
            if (ret != napi_ok) {
                continue;
            }
            strMap.insert(std::make_pair(it->first, str));
        }
    }
    return ret;
}

napi_value JS_Buffer2JbyteArray(napi_env env, const AutoBuffer& ab) {
    unsigned int len = ab.Length();

    if (len == 0) {
        return nullptr;
    }
    napi_value result;
    void * data = nullptr;
    napi_status status = napi_create_arraybuffer(env, len, &data, &result);
    if (status != napi_ok) {
        return nullptr;
    }

    memcpy(data, ab.Ptr(), len);
    return result;
}

napi_value JS_Buffer2JbyteArray(napi_env env, const void* buffer, size_t length) {
    unsigned int len = length;

    if (len == 0) {
        return nullptr;
    }

    napi_value result;
    void * data;
    napi_status status = napi_create_arraybuffer(env, len, &data, &result);
    if (status != napi_ok) {
        return nullptr;
    }
    memcpy(data, buffer, len);
    return result;
}

bool JS_JbyteArray2Buffer(napi_env env, napi_value arraybuffer, AutoBuffer& ab) {
    if (env == NULL) {
        return false;
    }

    if (arraybuffer == NULL) {
        return true;
    }
    void * data;
    size_t byte_length;
    napi_status status = napi_get_arraybuffer_info(env, arraybuffer, &data, &byte_length);

    ASSERT(byte_length > 0);
    ab.Write(data, byte_length);

    return true;
}


static std::mutex m;
static std::condition_variable cv;
static bool ready = false;

void RunJs(napi_env env, napi_value js_callback, void* context, void* data) {
    (void) context;
    xinfo2("enter CallJs");

    (((ExecData*)data)->jsfn)(data);

    {
        std::unique_lock<std::mutex> lock(m);
        ready = true;
    }

    xinfo2("exit CallJs and call notify_all()");
    cv.notify_all();
}

void WaitForRunJS(void* data) {
    xinfo2("enter WaitForExecJS");
    VarCache* cache_instance = VarCache::Singleton();
    napi_env env = cache_instance->GetJSEnv();

    napi_value work_name;
    napi_status status = napi_create_string_utf8(env, "js_work", NAPI_AUTO_LENGTH, &work_name);
    assert(status == napi_ok);

    napi_threadsafe_function tsfn;
    xinfo2("calling napi_create_threadsafe_function");
    status = napi_create_threadsafe_function(env, NULL, NULL, work_name, 0, 1, NULL, NULL, NULL, RunJs, &tsfn);
    assert(status == napi_ok);

    xinfo2("calling napi_call_threadsafe_function");
    status = napi_call_threadsafe_function(tsfn, data, napi_tsfn_blocking);
    assert(status == napi_ok);

    xinfo2("wait notify");
    std::unique_lock<std::mutex> lock(m);
    ready = false;
    while(!ready) {
        cv.wait(lock);
    }

    xinfo2("calling napi_release_threadsafe_function");
    status = napi_release_threadsafe_function(tsfn, napi_tsfn_release);
    assert(status == napi_ok);

    xinfo2("wait notify finished");
}

